package com.ease.util {

   import flash.utils.Dictionary;


   /**
    * Represents a projected version of a given object. A projected version contains the
    * properties values of the original object, but the property names can be different.
    * For example, one can define a projection y of an object x, such that: y.a == x.u,
    * y.b == x.u, y.c == x.v, etc.
    *
    * @internal Do not define any properties on this class since that could result in name
    * conflicts with the projected properties.
    */
   public dynamic class ObjectProjection
   {
      private var _projectedPropCount:int = 0;
      private var _originalByRedefinedPropName:Dictionary = new Dictionary();

      /**
       * Adds a new property mapping to the projection.
       */
      public function addProjection( redefinedPropName:String,
                                     originalPropName:String ):void {
         _originalByRedefinedPropName[redefinedPropName] = originalPropName;
      }

      /**
       * Removes an existing property mapping from the projection.
       */
      public function removeProjection( redefinedPropName:String ):void {
         delete _originalByRedefinedPropName[redefinedPropName];
      }

      /**
       * Evaluates the projection using the given object as the basis. Projection property
       * mappings should be added before calling this.
       */
      public function evaluate( source:Object ):Boolean {
         _projectedPropCount = 0;
         var isModified:Boolean = false;
         for (var redefinedPropName:String in _originalByRedefinedPropName) {
            var srcPropName:String = _originalByRedefinedPropName[redefinedPropName];
            var oldValue:Object = null;
            var newValue:Object = null;
            if (this.hasOwnProperty( redefinedPropName )) {
               oldValue = this[redefinedPropName];
            }
            if (source.hasOwnProperty( srcPropName )) {
               newValue = source[srcPropName];
               _projectedPropCount++;
            }
            this[redefinedPropName] = newValue;
            isModified ||= (oldValue != newValue);
         }
         return isModified;
      }

      /**
       * Provides a list of the original property names that have been redefined as or
       * mapped onto some other property names.
       */
      public function getProjectionSrcPropNames():Array/*String*/ {
         var srcPropNames:Array/*String*/ =
            MixedUtil.getValues( _originalByRedefinedPropName );
         return srcPropNames;
      }

      /**
       * Returns the total number of projected properties, as resulting from the last
       * evaluation of this projection.
       */
      public function getProjectedPropCount():int {
         return _projectedPropCount;
      }
   }

}